package com.googlecode.animator.controller
{
	import com.googlecode.animator.controller.mediators.SWFLoaderMediator;
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.animator.data.PositionObject3D;
	import com.googlecode.animator.data.markers.KeyframeVO;
	import com.googlecode.animator.data.materials.BitmapFileMaterialVO;
	import com.googlecode.animator.data.materials.ColorMaterialVO;
	import com.googlecode.animator.data.materials.MaterialVO;
	import com.googlecode.animator.data.materials.MovieMaterialVO;
	import com.googlecode.animator.data.materials.VideoStreamMaterialVO;
	import com.googlecode.animator.data.objects3d.CameraProxyVO;
	import com.googlecode.animator.data.objects3d.CubeVO;
	import com.googlecode.animator.data.objects3d.IObject3DVO;
	import com.googlecode.animator.data.objects3d.Object3DVO;
	import com.googlecode.animator.data.objects3d.PlaneVO;
	import com.googlecode.animator.data.objects3d.TargetProxyVO;
	import com.googlecode.animator.utils.UniqueID;
	import com.googlecode.animator.view.cameraProxies.CameraProxy;
	import com.googlecode.animator.view.cameraProxies.TargetProxy;
	
	import org.papervision3d.core.geom.Lines3D;
	import org.papervision3d.core.geom.renderables.Line3D;
	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.materials.BitmapFileMaterial;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.special.LineMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.scenes.Scene3D;
	import org.puremvc.as3.interfaces.INotification;

	/**
	 * Adds a 3D object to the model. 
	 * @author USER
	 * 
	 */
	public class AddObject3DCommand extends BaseCommand
	{
		private var _object3D:IObject3DVO;
		private var _mediatorsList:Array;
		private var _videoStreamList:Array;
		
		/**
		 * Executes command. 
		 * @param notification
		 * 
		 */
		override public function execute(notification:INotification):void
		{
			
			_object3D = notification.getBody() as IObject3DVO;
			_mediatorsList = new Array();
			_videoStreamList = new Array();
			
			var scene:Scene3D = settings.scene;
			var stub:String;
			var object3D:DisplayObject3D;

			// add object to scene
			if(_object3D is CameraProxyVO)
			{
				stub = "cameraProxy_";
				
				object3D = new CameraProxy();
			}
			
			if(_object3D is TargetProxyVO)
			{
				stub = "targetProxy_";
				
				object3D = new TargetProxy();
			}
			
			if(_object3D is PlaneVO)
			{
				stub = "plane_";
				
				object3D = handlePlane(_object3D as PlaneVO);
			}
			
			if(_object3D is CubeVO)
			{
				stub = "cube_";
				
				object3D = handleCube(_object3D as CubeVO);
			}
			
			if((_object3D as PositionObject3D).name == null)
			{
				(_object3D as PositionObject3D).name = getName(stub);
			}
			
			object3D.name = (_object3D as Object3DVO).name;
			
			/* object3D.x = (_object3D as Object3DVO).x;
			object3D.y = (_object3D as Object3DVO).y;
			object3D.z = (_object3D as Object3DVO).z; */
			
			object3D.rotationX = (_object3D as Object3DVO).rotationX;
			object3D.rotationY = (_object3D as Object3DVO).rotationY;
			object3D.rotationZ = (_object3D as Object3DVO).rotationZ;	
			
			var kFrame:KeyframeVO;

			for each(var kf:KeyframeVO in (_object3D as Object3DVO).keyframes)
			{
				kFrame = kf.clone() as KeyframeVO;
				kFrame.object3DName = (_object3D as Object3DVO).name;
				
				if(kFrame.time == 0)
				{
					object3D.x = kFrame.position.x;
					object3D.y = kFrame.position.y;
					object3D.z = kFrame.position.z;
				}

				(_object3D as Object3DVO).keyframes[kFrame.id] = kFrame;
			}
			
			
			if(kFrame == null)
			{
				kFrame = new KeyframeVO();
				kFrame.id = getName("keyframe_");
				kFrame.object3DName = (_object3D as Object3DVO).name;
				kFrame.isDraggable = false;
				kFrame.position.x = (_object3D as Object3DVO).x;
				kFrame.position.y = (_object3D as Object3DVO).y;
				kFrame.position.z = (_object3D as Object3DVO).z;
				
				object3D.x = kFrame.position.x;
				object3D.y = kFrame.position.y;
				object3D.z = kFrame.position.z;
				
				
				(_object3D as Object3DVO).keyframes[kFrame.id] = kFrame;
				
			}
			scene.addChild(object3D);
			
			(_object3D as Object3DVO).calculateTweens();
			
			settings.addObject3D(_object3D as Object3DVO);

			/* object3D.x = (_object3D as Object3DVO).keyframes[0].x;
			object3D.y = (_object3D as Object3DVO).keyframes[0].y;
			object3D.z = (_object3D as Object3DVO).keyframes[0].z; */
			
			//trace("Notification Calculate Tweens about to be sent");
			//trace("Notification Calculate Tweens sent");
				
			

			// create a position keyframe value object
			/* var kf:KeyframeVO = new KeyframeVO();
			kf.object3DName = (_object3D as Object3DVO).name;
			
			kf.position.x = (_object3D as Object3DVO).x;
			kf.position.y = (_object3D as Object3DVO).y;
			kf.position.z = (_object3D as Object3DVO).z;
			
			// by default the time must be zero	
			kf.time = 0;
			
			// the first keyframe should be non-draggable
			kf.isDraggable = false; */
			
			
			//settings.selectedObject3D = (_object3D as Object3DVO).name;
			
			
			
			// once all the objects have been added to the model we can look
			// after the more difficult materials, VideoStreamMaterial and MovieMaterial
			
			for(var i:uint=0; i<_mediatorsList.length; i++)
			{
				facade.registerMediator(_mediatorsList[i]);
			}
			
			for(i=0; i<_videoStreamList.length; i++)
			{
				settings.setObject3DMaterial(_videoStreamList[i]);
			}
			sendNotification(Notifications.TIMELINE_CALCULATE_TWEENS, (_object3D as Object3DVO).name);
			sendNotification(Notifications.OBJECT3D_SELECTED, (_object3D as Object3DVO).name);
			//sendNotification(Notifications.TIMELINE_ADD_KEYFRAME, kFrame);
		}
		
		protected function getName(stub:String):String
		{
			var uniqueID:UniqueID = new UniqueID();
			
			return stub + uniqueID.generateID();
		}
		/**
		 * Returns a plane with visible edges. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handlePlane(VO:PlaneVO):Plane
		{
			var material:* = getMaterial(VO.material);
			
			var plane:Plane = new Plane(material, VO.width, VO.height, VO.segmentsH, VO.segmentsW);
			// since planes have no thickness when viewed edge on, they will have lines around edges.
			var lm:LineMaterial = new LineMaterial(0x909090, 1);
			var lines:Lines3D = new Lines3D(lm);
			lines.name = "outline";
			
			var vt1:Vertex3D;
			var vt2:Vertex3D;
			
			var halfWidth:int = VO.width / 2;
			var halfHeight:int = VO.height / 2;
			
			var lineOffset:int = 20;
			
			var l:Line3D;
			
			//Top and bottom
			vt1 = new Vertex3D(-halfWidth -lineOffset, halfHeight + lineOffset, 0);
			vt2 = new Vertex3D(halfWidth + lineOffset, halfHeight + lineOffset, 0);
			
			l = new Line3D(lines, lm, 4, vt1, vt2);
			
			lines.addLine(l);
			
			vt1 = new Vertex3D(-halfWidth - lineOffset, -halfHeight - lineOffset, 0);
			vt2 = new Vertex3D(halfWidth + lineOffset, -halfHeight - lineOffset, 0);
			
			l = new Line3D(lines, lm, 4, vt1, vt2);
			
			lines.addLine(l);
			
			//Sides
			vt1 = new Vertex3D(-halfWidth - lineOffset, halfHeight + lineOffset, 0);
			vt2 = new Vertex3D(-halfWidth - lineOffset, -halfHeight - lineOffset, 0);
			
			l = new Line3D(lines, lm, 4, vt1, vt2);
			
			lines.addLine(l);
			
			vt1 = new Vertex3D(halfWidth + lineOffset, halfHeight + lineOffset, 0);
			vt2 = new Vertex3D(halfWidth + lineOffset, -halfHeight - lineOffset, 0);
			
			l = new Line3D(lines, lm, 4, vt1, vt2);
			
			lines.addLine(l);
			
			plane.addChild(lines);
			
			return plane;
		}
		
		/**
		 * Returns a cube. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handleCube(VO:CubeVO):Cube
		{
			var mList:MaterialsList = new MaterialsList();
			
			
			for each(var mat:MaterialVO in VO.materialsList)
			{
				mList.addMaterial(getMaterial(mat), mat.face);
			}  

			var cube:Cube = new Cube(mList, VO.width, VO.depth, VO.depth);
			
			return cube;
		}
		
		/**
		 * Returns the actual material needed for the object3D. 
		 * @param matVO
		 * @return 
		 * 
		 */
		protected function getMaterial(matVO:MaterialVO):*
		{
			var material:*;
			
			if(matVO is BitmapFileMaterialVO)
			{
				material = new BitmapFileMaterial((matVO as BitmapFileMaterialVO).filePath);
			}
			
			if(matVO is ColorMaterialVO)
			{
				material = new ColorMaterial();
				material.fillAlpha = (matVO as ColorMaterialVO).fillAlpha;
				material.fillColor = (matVO as ColorMaterialVO).fillColor;
			}
			
			if(matVO is MovieMaterialVO)
			{
				material = new ColorMaterial();
				
				var clone3D:Object3DVO = _object3D.clone() as Object3DVO;
				
				if(_object3D is CubeVO)
				{	
					(clone3D as CubeVO).selectedFace = matVO.face;
				}
						
				_mediatorsList.push(new SWFLoaderMediator(clone3D));
			}
			
			if(matVO is VideoStreamMaterialVO)
			{
				material = new ColorMaterial();
					
				/* if(_object3D as CubeVO)
				{
					object3D = (_object3D as CubeVO).clone() as CubeVO;
					(object3D as CubeVO).selectedFace = matVO.face;
				}else
				{
					object3D = (_object3D as Object3DVO).clone() as Object3DVO;
				} */
				
				_videoStreamList.push(_object3D.clone());
			}
			/* switch(matVO.type)
			{
				case ProjectConstants.BITMAP_FILE_MATERIAL:
				
					
					
					break;
					
				case ProjectConstants.COLOR_MATERIAL:
					
					
					
					break;
				
				case ProjectConstants.MOVIE_MATERIAL:
				
					material = new ColorMaterial();
					
					var object3D:IObject3DVO
					
					if(_object3D as CubeVO)
					{
						object3D = (_object3D as CubeVO).clone() as CubeVO;
						(object3D as CubeVO).selectedFace = matVO.face;
					}else
					{
						object3D = (_object3D as Object3DVO).clone() as Object3DVO;
					}
										
					_mediatorsList.push(new SWFLoaderMediator(object3D));
					
					break;
					
				case ProjectConstants.VIDEO_STREAM_MATERIAL:
					
					material = new ColorMaterial();
					
					if(_object3D as CubeVO)
					{
						object3D = (_object3D as CubeVO).clone() as CubeVO;
						(object3D as CubeVO).selectedFace = matVO.face;
					}else
					{
						object3D = (_object3D as Object3DVO).clone() as Object3DVO;
					}
					
					_videoStreamList.push(object3D);
					
					break;
			} */
			
			/* material.fillAlpha = matVO.fillAlpha;
			material.fillColor = matVO.fillColor;
			material.interactive = matVO.interactive;
			material.invisible = matVO.invisible;
			material.lineAlpha = matVO.lineAlpha;
			material.lineColor = matVO.lineColor;
			material.lineThickness = matVO.lineThickness;
			material.name = matVO.name;
			material.oneSide = matVO.oneSide;
			material.opposite = matVO.opposite; */
			
			return material;
		}
		
	}
}